package com.duolebo.uteped_sdk.remind;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.media.AudioManager;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
import android.os.Build;
import android.provider.ContactsContract;
import android.telecom.TelecomManager;
import android.telephony.PhoneStateListener;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

import com.android.internal.telephony.ITelephony;
import com.duolebo.uteped_sdk.service.UTEPedNotificationService;
import com.duolebo.uteped_sdk.utils.Const;
import com.duolebo.uteped_sdk.utils.Logger;
import com.duolebo.uteped_sdk.utils.Tools;
import com.google.gson.Gson;
import com.yc.nadalsdk.bean.HangupOperation;
import com.yc.nadalsdk.bean.MessageInfo;
import com.yc.nadalsdk.bean.Notify;
import com.yc.nadalsdk.bean.RejectMessage;
import com.yc.nadalsdk.bean.Response;
import com.yc.nadalsdk.ble.open.UteBleDevice;
import com.yc.nadalsdk.constants.NotifyType;
import com.yc.nadalsdk.constants.RemindType;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;

/**
 * @Author: LiuYang
 * @Date: 2023/12/24 10:51
 * @Description: 对应来电提醒功能部分
 */
public class CallRemind extends BaseRemind {

    private final static String TAG = "CallRemind";

    private final Context context;
    private TelephonyManager telephonyManager;

    private int lastState = TelephonyManager.CALL_STATE_IDLE;
    private Date callStartTime = new Date();
    private boolean isIncoming = false;
    private String savedNumber = "";


    @SuppressLint("StaticFieldLeak")
    private static CallRemind instance;
    public static CallRemind getInstance(Context context) {
        if (instance == null) {
            instance = new CallRemind(context);
        }

        return instance;
    }

    public static CallRemind shareInstance() {
        return instance;
    }

    public CallRemind(Context context) {
        super(context);
        this.context = context;
        if (Tools.isXiaoMi()) {
            createPhoneListener();
        }
    }

    /**
     * 创建电话监听
     */
    public void createPhoneListener() {
        telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        //设置一个通话状态监听器
        telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    }

    public void onDestroy() {
        if (telephonyManager != null) {
            if (phoneStateListener != null) {
                telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
            }
        }
    }

    PhoneStateListener phoneStateListener = new PhoneStateListener() {
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            super.onCallStateChanged(state, incomingNumber);
            Logger.d(TAG, "state: " + state + " number:" + incomingNumber);
            CallRemind.this.onCallStateChanged(context, state, incomingNumber);
        }
    };

    /**
     * 来电
     *
     * @param incomingNumber incomingNumber
     * @param name           name
     */
    public void call(String incomingNumber, String name) {
        MessageInfo msgInfo = new MessageInfo();
        msgInfo.setType(RemindType.TYPE_CALL);
        msgInfo.setPhoneNumber(incomingNumber);
        msgInfo.setTitle(name);

        sendMessageInfo(msgInfo);
    }

    private void acceptCall() {
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
                telecomManager.acceptRingingCall();
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                MediaSessionManager mediaSessionManager = (MediaSessionManager)context.getSystemService(Context.MEDIA_SESSION_SERVICE);
                List<MediaController> mediaControllerList = mediaSessionManager.getActiveSessions(new ComponentName(context, UTEPedNotificationService.class));
                for (MediaController m:mediaControllerList) {
                    if ("com.android.server.telecom".equals(m.getPackageName())) {
                        m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
                        m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
                        break;
                    }
                }
            } else {
                AudioManager audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
                KeyEvent eventDown = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK);
                KeyEvent eventUp = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK);
                audioManager.dispatchMediaKeyEvent(eventDown);
                audioManager.dispatchMediaKeyEvent(eventUp);
                Runtime.getRuntime().exec("input keyevent " + Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean endCall() {
        // 这里写一个挂断电话
        try {
            TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ANSWER_PHONE_CALLS) != PackageManager.PERMISSION_GRANTED) {
                    Log.e(TAG, "cmdCallPhone: no permission");
                    return false;
                }
                // 9.0后提供了 telecomManager.endCall 挂断电话
                telecomManager.endCall();
                Logger.d(TAG, "*** endCall ok ***");
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, "cmdCallPhone Exception:" + e);
        }

        return true;
    }

    public void tryEndCall() {
        endCall();
        // 这里写一个挂断电话
        try {
            Logger.d(TAG, "ITelephony  endCall");
            //9.0之前通过反射调用endCall挂断电话的方法(部分机型不起作用)
            TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            Class c = Class.forName(tm.getClass().getName());
            Method m = c.getDeclaredMethod("getITelephony");
            m.setAccessible(true);
            ITelephony telephonyService = (ITelephony) m.invoke(tm);//aidl创建导包后依然报错,运行项目后即可
            if (telephonyService != null) {
                telephonyService.endCall();
                Logger.d(TAG, "*** ITelephony.endCall ok ***");
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, "cmdCallPhone Exception:" + e);
        }
    }


    private boolean killPhoneCall() {
        try {

            TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            Class classTelephony = Class.forName(telephonyManager.getClass().getName());
            Method methodGetITelephony = classTelephony.getDeclaredMethod("getITelephony");
            methodGetITelephony.setAccessible(true);
            Object telephonyInterface = methodGetITelephony.invoke(telephonyManager);
            Class telephonyInterfaceClass =
                    Class.forName(telephonyInterface.getClass().getName());
            Method methodEndCall = telephonyInterfaceClass.getDeclaredMethod("endCall");
            methodEndCall.invoke(telephonyInterface);
        } catch (Exception e) {
            e.printStackTrace();
            Logger.d(TAG, "hangupPhoneCall: " + e.toString());
            return false;
        }
        return true;
    }

    public void setSavedNumber(String number) {
        this.savedNumber = number;
    }

    public void onCallStateChanged(Context context, int state, String number) {
        if (lastState == state || TextUtils.isEmpty(number)) {
            return;
        }

//        boolean enable = SPHelper.shareInstance().getBoolean(Const.M_setCallRemindEnable, true);
//        if (!enable) {
//            return;
//        }

        switch (state) {
            case TelephonyManager.CALL_STATE_RINGING: {
                isIncoming = true;
                callStartTime = new Date();
                savedNumber = number;
                onIncomingCallReceived(context, number);
            }
            break;

            case TelephonyManager.CALL_STATE_OFFHOOK: {
                if (lastState != TelephonyManager.CALL_STATE_RINGING) {
                    isIncoming = false;
                    callStartTime = new Date();

                    onOutgoingCallStarted(context, savedNumber);
                } else {
                    isIncoming = true;
                    callStartTime = new Date();

                    onIncomingCallAnswered(context, savedNumber);
                }
            }
            break;

            case TelephonyManager.CALL_STATE_IDLE: {
                if (lastState == TelephonyManager.CALL_STATE_RINGING) {
                    //Ring but no pickup
                    onMissedCall(context, savedNumber);
                } else if (isIncoming) {
                    onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
                } else {
                    onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
                }
            }
            break;
        }

        lastState = state;
    }

    public synchronized String getNameByPhoneNumber(Context context, String number) {
        String[] projection = {ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER};
        String displayName = "Unknown";
        if (TextUtils.isEmpty(number))
            return displayName;

        try {
            String phone1 = number.subSequence(0, 3) + " " + number.substring(3, 7) +
                    " " + number.substring(7, 11);
            String phone2 = number.subSequence(0, 3) + "-" + number.substring(3, 7) +
                    "-" + number.substring(7, 11);
            ContentResolver resolver = context.getContentResolver();
            Cursor cursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, ContactsContract.CommonDataKinds.Phone.NUMBER + " in(?,?,?)", new String[]{
                    number, phone1, phone2}, null);

            if (cursor != null) {
                while (cursor.moveToNext()) {
                    int columnIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
                    displayName = cursor.getString(columnIndex);
                    if (!TextUtils.isEmpty(displayName)) {
                        break;
                    }
                    cursor.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return displayName;
    }

    private void onOutgoingCallEnded(Context context, String number, Date callStartTime, Date date) {
        Response<?> response = this.uteBleConnection.notifyHangup();
        Logger.d(TAG, "onOutgoingCallEnded response =" + new Gson().toJson(response));
    }

    private void onIncomingCallEnded(Context context, String number, Date callStartTime, Date date) {
        Response<?> response = this.uteBleConnection.notifyHangup();
        Logger.d(TAG, "onIncomingCallEnded response =" + new Gson().toJson(response));
    }

    private void onMissedCall(Context context, String number) {
        Response<?> response = this.uteBleConnection.notifyHangup();
        Logger.d(TAG, "onMissedCall hangup response =" + new Gson().toJson(response));
        String name = getNameByPhoneNumber(context, number);
        Logger.d(TAG, "onMissedCall name:" + name + " number:" + number);
        response = this.uteBleConnection.notifyMissedCall(name, number);
        Logger.d(TAG, "onMissedCall response =" + new Gson().toJson(response));
    }

    private void onIncomingCallAnswered(Context context, String number) {
        Response<?> response = this.uteBleConnection.notifyAnswerCall();
        Logger.d(TAG, "onIncomingCallAnswered response =" + new Gson().toJson(response));
    }

    private void onOutgoingCallStarted(Context context, String number) {
        String name = getNameByPhoneNumber(context, number);
        Logger.d(TAG, "onOutgoingCallStarted name:" + name + " number:" + number);
        Response<?> response = this.uteBleConnection.notifyOutgoingCall(name, number);
        Logger.d(TAG, "onOutgoingCallStarted response =" + new Gson().toJson(response));
    }

    private void onIncomingCallReceived(Context context, String number) {
        String name = getNameByPhoneNumber(context, number);
        Logger.d(TAG, "onOutgoingCallStarted name:" + name + " number:" + number);
        Response<?> response = this.uteBleConnection.notifyIncomingCall(number, number);
        Logger.d(TAG, "onIncomingCallReceived response =" + new Gson().toJson(response));
    }

    private void replyMessage(String message, String phoneNumber) {
        if (TextUtils.isEmpty(message) || TextUtils.isEmpty(phoneNumber)) {
            Logger.d(TAG, "message or  phone is empty");
            return;
        }
        try {
            SmsManager smsManager = SmsManager.getDefault();
            smsManager.sendTextMessage(phoneNumber, null, message, null, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Override
    public void onNotify(@NonNull UteBleDevice uteBleDevice, @NonNull Notify notify) {
        int eventType = notify.getType();
        switch (eventType) {
            case NotifyType.INCOMING_CALL_REJECT:
                Logger.d(TAG, "onNotify incoming call reject");
                tryEndCall();

                if (notify.getErrorCode() == Const.CODE_SUCCESS) {
                    RejectMessage rejectMessage = (RejectMessage) notify.getData();
                    replyMessage(rejectMessage.getContent(), rejectMessage.getNumber());
                }
                break;

            case NotifyType.INCOMING_CALL_OPERATION:
                Logger.d(TAG, "onNotify incoming call operation");
                if (notify.getErrorCode() == Const.CODE_SUCCESS) {
                    HangupOperation hangupOperation = (HangupOperation) notify.getData();
                    if (hangupOperation.getType() == HangupOperation.HANG_UP) {
                        tryEndCall();
                    }
                }

                break;

            default:
        }
    }
}
